﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace snowflake_雪花Id
{

    /// <summary>
    /// 雪花Id
    /// 组成格式为：时间戳|序号|数据中心ID|服务器ID
    /// </summary>
    public class SnowFlakeId
    {
        /// <summary>
        /// 数据中心Id长度
        /// </summary>
        public int DataCenterIdBitLength { get; private set; }

        /// <summary>
        /// 服务器Id长度
        /// </summary>
        public int WorkerIdBitLength { get; private set; }

        /// <summary>
        /// 自增长序号长度
        /// </summary>
        public int SequenceBitLength { get; private set; }

        /// <summary>
        /// 时间戳长度
        /// </summary>
        public int TimestampBitLength { get; private set; }

        /// <summary>
        /// 数据中心Id最大值
        /// </summary>
        public long MaxDataCenterId { get; private set; }

        /// <summary>
        /// 服务器Id最大值
        /// </summary>
        public long MaxWorkerId { get; private set; }

        /// <summary>
        /// 自增序号最大值
        /// </summary>
        public long MaxSequence { get; private set; }

        /// <summary>
        /// 时间戳最大值
        /// </summary>
        public long MaxTimestamp { get; private set; }

        public DateTime MaxUtcTime { get { return Jan1st1970.AddMilliseconds(MaxTimestamp + StartTimestamp); } }

        /// <summary>
        /// 上次生成ID
        /// </summary>
        public SnowIdClass LastSnowId { get; private set; }

        /// <summary>
        /// 数据中心Id
        /// </summary>
        public long DataCenterId { get; private set; }

        /// <summary>
        /// 服务器Id
        /// </summary>
        public long WorkerId { get; private set; }

 
        public SnowFlakeId() : this(16,0,0,0,0)
        {

        }

        public SnowFlakeId(long dataCenterId, long workerId) : this(12, 5, 5, dataCenterId, workerId)
        {

        }

        public SnowFlakeId(int sequenceBitLength, int datacenterIdBitLength, int workerIdBitLength, long dataCenterId, long workerId)
        {
            this.SequenceBitLength = sequenceBitLength;
            this.DataCenterIdBitLength = datacenterIdBitLength;
            this.WorkerIdBitLength = workerIdBitLength;
            this.TimestampBitLength = Marshal.SizeOf<long>() - (SequenceBitLength + DataCenterIdBitLength + WorkerIdBitLength);
        
            this.MaxSequence = GetBitLengthMaxValue(this.SequenceBitLength);
            this.MaxDataCenterId=GetBitLengthMaxValue(this.DataCenterIdBitLength);
            this.MaxWorkerId = GetBitLengthMaxValue(this.WorkerIdBitLength);
            this.MaxTimestamp = GetBitLengthMaxValue(this.TimestampBitLength);

            if (dataCenterId > MaxDataCenterId) throw new OverflowException(nameof(dataCenterId));
            if (workerId > MaxWorkerId) throw new OverflowException(nameof(workerId));

            this.DataCenterId = dataCenterId;
            this.WorkerId = workerId;

            this.LastSnowId = new SnowIdClass()
            {
                Timestamp=0L,
                Sequence = 0L,
                DataCenterId = this.DataCenterId,
                WorkerId = this.WorkerId,
            };

        }

        /// <summary>
        /// 生成ID
        /// </summary>
        /// <returns></returns>
        public long GenerateId()
        {
            lock (LastSnowId)
            {
                long timestamp = GetCurrentTimestamp();
                if (timestamp > LastSnowId.Timestamp) //时间戳改变，毫秒内序列重置
                {
                    LastSnowId.Sequence = 0L;
                }
                else if (timestamp == LastSnowId.Timestamp) //如果是同一时间生成的，则进行毫秒内序列
                {
                    LastSnowId.Sequence += 1L;
                    if (LastSnowId.Sequence > MaxSequence) //毫秒内序列溢出
                    {
                        LastSnowId.Sequence = 0L;
                        timestamp = GetNextTimestamp(LastSnowId.Timestamp); //阻塞到下一个毫秒,获得新的时间戳
                    }
                }
                else   //当前时间小于上一次ID生成的时间戳，证明系统时钟被回拨，此时需要做回拨处理
                {
                    LastSnowId.Sequence += 1L;
                    timestamp = LastSnowId.Timestamp; //停留在最后一次时间戳上，等待系统时间追上后即完全度过了时钟回拨问题。
                    if (LastSnowId.Sequence > MaxSequence)  //毫秒内序列溢出
                    {
                        LastSnowId.Sequence = 0L;
                        timestamp = LastSnowId.Timestamp + 1L;   //直接进位到下一个毫秒                          
                    }
                    //throw new Exception(string.Format("Clock moved backwards.  Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp));
                }
                LastSnowId.Timestamp = timestamp;       //上次生成ID的时间戳
                LastSnowId.Id = GenerateId(LastSnowId);
                return LastSnowId.Id;
            }
        }

        /// <summary>
        /// 生成ID
        /// </summary>
        /// <param name="timestamp">时间戳</param>
        /// <param name="sequence">序号</param>
        /// <returns></returns>
        public long GenerateId(SnowIdClass snowId)
        {
            //移位并通过或运算拼到一起组成64位的ID
            var id = (snowId.Timestamp << (SequenceBitLength + DataCenterIdBitLength + WorkerIdBitLength))
                    | (snowId.Sequence << (DataCenterIdBitLength + WorkerIdBitLength))
                    | (snowId.DataCenterId << WorkerIdBitLength)
                    | (snowId.WorkerId);

            return id;
        }

        /// <summary>
        /// 解析雪花ID
        /// </summary>
        /// <returns></returns>
        public SnowIdClass AnalyzeId(long Id)
        {
            var snowIdClass = new SnowIdClass()
            {
                Id = Id,
                Timestamp = Id >> (SequenceBitLength + DataCenterIdBitLength + WorkerIdBitLength),
                Sequence = Id >> (DataCenterIdBitLength + WorkerIdBitLength) & MaxSequence,
                DataCenterId = Id >> WorkerIdBitLength & MaxDataCenterId,
                WorkerId = Id & MaxWorkerId
            };
            return snowIdClass;                  
        }

        private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        // 开始时间戳 
        public static readonly long StartTimestamp = (long)(new DateTime(2020, 1, 1,0,0,0,DateTimeKind.Utc) - Jan1st1970).TotalMilliseconds;

        /// <summary>
        /// 阻塞到下一个毫秒，直到获得新的时间戳
        /// </summary>
        /// <param name="lastTimestamp">上次生成ID的时间戳</param>
        /// <returns>当前时间戳</returns>
        private static long GetNextTimestamp(long lastTimestamp)
        {
            long timestamp = GetCurrentTimestamp();
            while (timestamp <= lastTimestamp)
            {
                timestamp = GetCurrentTimestamp();
            }
            return timestamp;
        }

        /// <summary>
        /// 获取当前时间戳
        /// </summary>
        /// <returns></returns>
        private static long GetCurrentTimestamp()
        {
            return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds - StartTimestamp;
        }

        private static long GetBitLengthMaxValue(int length)
        {
            return -1L ^ (-1L << length);
        }


        /// <summary>
        /// 雪花ID结构
        /// </summary>
        public class SnowIdClass
        {
            /// <summary>
            /// ID
            /// </summary>
            public long Id { get; set; }
            /// <summary>
            /// 时间戳
            /// </summary>
            public long Timestamp { get; set; }

            /// <summary>
            /// Unix时间戳
            /// </summary>
            public long UnixTimestamp { get { return Timestamp + StartTimestamp; } }

            /// <summary>
            /// 生成时间
            /// </summary>
            public DateTime UtcDateTime { get { return Jan1st1970.AddMilliseconds(UnixTimestamp); } }

            /// <summary>
            /// 自增长序号
            /// </summary>
            public long Sequence { get; set; }

            /// <summary>
            /// 数据中心Id
            /// </summary>
            public long DataCenterId { get; set; }

            /// <summary>
            /// 服务器Id
            /// </summary>
            public long WorkerId { get; set; }

        }
    }
}
